// Alex Silkin

#pragma once
#ifndef KIT_ROBOT_AGENT_H
#define KIT_ROBOT_AGENT_H

#include <string>
#include <list>

#include "Agent.h"
#include "KitRobot.h"
#include "KitRobotGUIInterface.h"
#include "Table.h"
#include "Conveyor.h"
#include "PartRobot.h"
#include "Inspector.h"
#include <qpoint.h>

class Agent;
class KitRobot;
class PartRobot;
class Kit;
class KitRobotGUIInterface;
class Table;

class KitRobotAgent : public KitRobot, public Agent
{
public:
	KitRobotAgent(std::string name);

	~KitRobotAgent();


	// ------------------------------------------------------------------------------------
	// Messages:

	virtual void msgCanIGoToTable();										// Called by PartsAgent

	virtual void msgDoneDumpingParts();									// Called by PartsAgent

	virtual void msgHereIsKit(Kit* k);											// Called by ConveyourAgent

	virtual void msgKitFinished(Kit* k);										// Called by PartsAgent

	virtual void msgHereIsKitState(InspectorKitState state);		// Called InspectorKitStatus

	virtual void msgAnimationDone();											// Called by GUI

	// ------------------------------------------------------------------------------------


	// ------------------------------------------------------------------------------------
	//Utility Method(s)

	void completeScheduledAwake();

	// ------------------------------------------------------------------------------------

	// ------------------------------------------------------------------------------------
	// Scheduler
	virtual bool pickAndExecuteAnAction();
	// ------------------------------------------------------------------------------------


	// ------------------------------------------------------------------------------------
	// Accessors

	// Access the PartsRobot
	PartRobot* GetPartsRobot(void) 			{ return(partsRobot);		};
	void SetPartsRobot(PartRobot* _partsRobot)	{ partsRobot = _partsRobot;	};

	// Access the OnConveyor
	Conveyor* GetOnConveyor(void) 		{ return(onConveyor);		};
	void SetOnConveyor( Conveyor* _onConveyor)	{ onConveyor = _onConveyor;	};

	// Access the OffBadConveyor
	 Conveyor* GetOffBadConveyor(void) 			{ return(offBadConveyor);			};
	void SetOffBadConveyor( Conveyor* _offBadConveyor)	{ offBadConveyor = _offBadConveyor;	};


	// Access the OffGoodConveyor
	 Conveyor* GetOffGoodConveyor(void) 				{ return(offGoodConveyor);				};
	void SetOffGoodConveyor( Conveyor* _offGoodConveyor)	{ offGoodConveyor = _offGoodConveyor;	};

	// Access the Table
	 Table* GetTable(void) 	{ return(table);	};
	void SetTable(Table* _table)	{ table = _table;	};

	// Access the Inspector
	 Inspector* GetInspector(void) 		{ return(inspector);		};
	void SetInspector(Inspector* _inspector)	{ inspector = _inspector;	};

	// Access the Gui
	virtual KitRobotGUIInterface* GetGui(void) 	{ return(gui);	};
	void SetGui( KitRobotGUIInterface* _gui)	{ gui = _gui;	};

	// Access the AwakeScheduled
	bool GetAwakeScheduled(void) const				{ return(awakeScheduled);			};
	// ------------------------------------------------------------------------------------


	// Non-norms
	void handleBreak();
	void handleFix();
	// ------------------------------------------------------------------------------------

protected:
	virtual std::string toString() const;

private:

	// ------------------------------------------------------------------------------------
	//Data
	enum KitStatus {ReadyForTable, ReadyForInspection, ReadyForDelivery, NoAction};

	struct MyKit {
		Kit* kit;
		KitStatus status;

		MyKit(Kit* k, KitStatus s) {
			kit = k;
			status = s;
		}
	};

	class AwakeThread : public Thread {
	public:
		AwakeThread() {}
		AwakeThread(KitRobotAgent* robot) {
			myRobot = robot;
		}
	protected:
		virtual void run() {
			sleep(5);
			myRobot->completeScheduledAwake();
		}


	private:
		KitRobotAgent* myRobot;
	};

	bool awakeScheduled;
	AwakeThread awakeThread;

	PartRobot* partsRobot;
	Conveyor* onConveyor;
	Conveyor* offBadConveyor;
	Conveyor* offGoodConveyor;
	Table* table;
	Inspector* inspector;

	InspectorKitState inspectorState;

	Lock kitsListLock;
	std::list<MyKit*> myKits; // I don't think I need a pointer to a list, because the data is only accessed locally
	
	KitRobotGUIInterface* gui;

	Sema partsRobotSem;
	Sema animationSem;
	bool scheduledMsgTableIsFree;
	// ------------------------------------------------------------------------------------


	// ------------------------------------------------------------------------------------
	//Actions

	void takeKitOffConveyorAndPlaceOnKitStand(MyKit* myKit);
   
	void takeKitToInspection(MyKit* myKit);
   
	void takeKitToDeliveryStation();

	void tellPartsAgentTableIsFree();

	// ------------------------------------------------------------------------------------


	// ------------------------------------------------------------------------------------
	//Utility Method(s)

	MyKit* findKitByStatus (KitStatus status);		// Borrowed from FCSAgent. Thanks Anoop (I think)
	
	void scheduleAwake();	// Awake the agent after some time to check if table slots are available
	
	// ------------------------------------------------------------------------------------
};

#endif